package com.xxc.logger.handler

import com.xxc.common.util.IpUtil
import com.xxc.logger.annotation.Log
import com.xxc.logger.annotation.NoLog
import com.xxc.logger.enum.HeaderConstants
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.reflect.MethodSignature
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.ServletRequestAttributes
import org.springframework.web.multipart.MultipartFile
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import java.lang.reflect.Method

@Aspect
@Component
class LoggerAspect {

    private val logger = LoggerFactory.getLogger(this.javaClass)

    /**
     * 敏感字段列表（当然这里你可以更改为可配置的）
     */
    private val sensitiveFieldList: List<String>
        get() {
            val sensitiveFieldList = ArrayList<String>()
            sensitiveFieldList.add("pwd")
            sensitiveFieldList.add("password")
            return sensitiveFieldList
        }

    /**
     * 环绕通知
     * @param joinPoint 连接点
     * @return 切入点返回值
     * @throws Throwable 异常信息
     */
    @Around("@within(org.springframework.web.bind.annotation.RestController) || @annotation(org.springframework.web.bind.annotation.RestController) || @annotation(com.xxc.logger.annotation.Log)")
    @Throws(Throwable::class)
    fun apiLog(joinPoint: ProceedingJoinPoint): Any? {
        val signature = joinPoint.signature as MethodSignature
        val method = signature.method

        val logFlag = this.needToLog(method)
        if (!logFlag) {
            return joinPoint.proceed()
        }

        val request = (RequestContextHolder.getRequestAttributes() as ServletRequestAttributes).request
        val requester = request.session.getAttribute("auth")?:Any()
        val ip = IpUtil.getRealIp(request)
        val methodName = this.getMethodName(joinPoint)
        val params = this.getParamsJson(joinPoint)

        val callSource = request.getHeader(HeaderConstants.CALL_SOURCE)
        val appVersion = request.getHeader(HeaderConstants.APP_VERSION)
        val apiVersion = request.getHeader(HeaderConstants.API_VERSION)
        val userAgent = request.getHeader("user-agent")

        logger.info("Started request requester [{}] method [{}] params [{}] IP [{}] callSource [{}] appVersion [{}] apiVersion [{}] userAgent [{}]", requester, methodName, params, ip, callSource, appVersion, apiVersion, userAgent)
        val start = System.currentTimeMillis()
        val result = joinPoint.proceed()?:""
        logger.info("Ended request requester [{}] method [{}] params[{}] response is [{}] cost [{}] millis ",
                requester, methodName, params, result.toString(), System.currentTimeMillis() - start)
        return result
    }

    private fun getMethodName(joinPoint: ProceedingJoinPoint): String {
        var methodName = joinPoint.signature.toShortString()
        val shortMethodNameSuffix = "(..)"
        if (methodName.endsWith(shortMethodNameSuffix)) {
            methodName = methodName.substring(0, methodName.length - shortMethodNameSuffix.length)
        }
        return methodName
    }

    private fun getParamsJson(joinPoint: ProceedingJoinPoint): String {
        val args = joinPoint.args
        val sb = StringBuilder()
        for (arg in args) {
            if(arg == null)
                continue
            //移除敏感内容
            val paramStr: String
            if (arg is HttpServletResponse) {
                paramStr = HttpServletResponse::class.java.simpleName
            } else paramStr = when (arg) {
                is HttpServletRequest -> HttpServletRequest::class.java.simpleName
                is MultipartFile -> {
                    val size = arg.size
                    MultipartFile::class.java.simpleName + " size:" + size
                }
                else -> arg.toString()
            }
            sb.append(paramStr).append(",")
        }
        return if(sb.isNotEmpty())
            sb.deleteCharAt(sb.length - 1).toString()
        else
            sb.toString()
    }

    /**
     * 判断是否需要记录日志
     */
    private fun needToLog(method: Method): Boolean {
        if(method.getAnnotation(NoLog::class.java) != null)
            return false
        //如果方法为get请求则不打印，除非方法、类上有Log注解
        return method.getAnnotation(GetMapping::class.java) == null || method.getAnnotation(Log::class.java) != null
                || method.declaringClass.getAnnotation(Log::class.java) != null
    }

//    /**
//     * 删除参数中的敏感内容
//     * @param obj 参数对象
//     * @return 去除敏感内容后的参数对象
//     */
//    private fun deleteSensitiveContent(obj: Any?): String {
//        var jsonObject = JSONObject()
//        if (obj == null || obj is Exception) {
//            return jsonObject.toJSONString()
//        }
//
//        try {
//            val param = GsonUtil.toJson(obj)
//            jsonObject = JSONObject.parseObject(param)
//            val sensitiveFieldList = this.sensitiveFieldList
//            for (sensitiveField in sensitiveFieldList) {
//                if (jsonObject.containsKey(sensitiveField)) {
//                    jsonObject.put(sensitiveField, "******")
//                }
//            }
//        } catch (e: ClassCastException) {
//            return obj.toString()
//        }
//
//        return obj.toString()
//    }
}